from sqlalchemy import ForeignKey, CheckConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from database.models.base import str_256, created_at, updated_at, Sex, Base
from datetime import date


class User(Base):
    __tablename__ = "users"
    
    username: Mapped[str_256] = mapped_column(primary_key=True)
    password_hash: Mapped[str_256]
    name: Mapped[str_256]
    birth: Mapped[date]
    weight: Mapped[int]
    height: Mapped[int]
    sex: Mapped[Sex]
    
    purpose_name: Mapped[str] = mapped_column(
        ForeignKey(
            column="purposes.name", 
            ondelete="RESTRICT", 
            onupdate="CASCADE"
        ),
    )
    physical_activity_name: Mapped[str] = mapped_column(
        ForeignKey(
            column="physical_activities.name", 
            ondelete="RESTRICT", 
            onupdate="CASCADE"
        ),
    )
    
    created_at: Mapped[created_at]
    updated_at: Mapped[updated_at]
    
    purpose: Mapped["Purpose"] = relationship(  # noqa: F821
        back_populates="users",
    )
    physical_activity: Mapped["PhysicalActivity"]  = relationship(  # noqa: F821
        back_populates="users",
    )
    products: Mapped[list["Product"]] = relationship(  # noqa: F821
        back_populates="user",
    )
    weight_updates: Mapped[list["WeightUpdate"]] = relationship(  # noqa: F821
        back_populates="user",
    )
    
    __table_args__ = (
        CheckConstraint("weight > 0", name="check_weight_positive"),
        CheckConstraint("height > 0", name="check_height_positive"),
        CheckConstraint("birth < current_date", name="check_birth_not_future"),
    )
    
    
    def get_daily_norm(self) -> tuple:
        BMR = None # Basal Metabolic Rate
        cur_age = (date.today() - self.birth).days / 365
        w = self.weight
        h = self.height
        phys_coef = self.physical_activity.coef
        purp_coef = self.purpose.coef
        # Mifflin-St Jeor Equation
        if self.sex == Sex["Женщина"]:
            BMR = 10 * w + 6.25 * h - 5 * cur_age - 161
        else:
            BMR = 10 * w + 6.25 * h - 5 * cur_age + 5
            
        energy = BMR * phys_coef * purp_coef
        
        # If we need lose or gain weight, then we need to eat more proteins
        proteins_per_kg = phys_coef * (1 + abs(purp_coef - 1))
        proteins = self.weight * proteins_per_kg
        
        fats_per_kg = 0.67 * self.physical_activity.coef # 0.8 is norm
        fats = self.weight  * fats_per_kg
        
        carbs = (energy - proteins * 4.1 - fats * 9.3) / 4.1
            
        return (proteins, fats, carbs)
